/***************************************************************************
 *   Copyright (C) 2014 by Philipp Müller                                  *
 *   philipp.mueller@gmx.de                                                *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA          *
 ***************************************************************************/

#include <QQmlContext>

#include "vompremote.h"


// Main Class

VompRemote::VompRemote( QObject *parent ) : QObject( parent )
{
    m_serverTimeOffset = 0;
    m_vdrAddress = QHostAddress();
    m_vdrName = QString();

    m_currentHost = -1;
    m_colorButtonStyle = false;
    m_vdrEnabled = false;

    m_settings = new QSettings("Vomp", "VompRemote");

    m_mvpListModel = new MvpListModel( this, this );

    m_channelListModel = new ChannelListModel( this, this );

    m_timerListModel = new TimerListModel( this, this );

    loadSettings();

    m_udpSocket = new QUdpSocket( this );
    m_udpSocket->bind( QHostAddress::LocalHost );


    m_vdrFindServer = new VDRFindServer;
    m_vdrFindServer->moveToThread( &m_workerThread );

    m_vdr = new VDR;
    m_vdr->moveToThread( &m_workerThread );

    connect( &m_workerThread, &QThread::finished, m_vdr, &QObject::deleteLater );

    connectSignals();

    m_workerThread.start();

    if ( m_vdrEnabled )
    {
        loginVDR();
    }
}


VompRemote::~VompRemote()
{
    m_workerThread.quit();
    m_workerThread.wait();
    delete m_udpSocket;
    delete m_settings;
}


void VompRemote::connectSignals()
{
    qRegisterMetaType< QHostAddress >( "QHostAddress" );
    qRegisterMetaType< ChannelList >( "ChannelList" );
    qRegisterMetaType< ScheduleList >( "ScheduleList" );
    qRegisterMetaType< TimerEntry >( "TimerEntry" );
    qRegisterMetaType< QList<TimerEntry> >( "QList<TimerEntry>" );


    //Error messages
    connect( m_vdr, &VDR::messageError,
             this,  &VompRemote::messageError );
    connect( m_vdrFindServer, &VDRFindServer::messageError,
             this,  &VompRemote::messageError );

    connect( m_vdr, &VDR::connectedChanged,
             this,  &VompRemote::vdrConnectionStateChanged );


    //TV Channellist
    connect( this,  &VompRemote::getChannelList,
             m_vdr, &VDR::getChannelList );

    connect( m_vdr, &VDR::channelListAvailable,
             this,  &VompRemote::updateChannels );

    //TV Schedules
    //    connect( m_channelListModel, &ChannelListModel::getSchedule,
    //             m_vdr,              &VDR::getSchedule );
    connect( m_channelListModel, SIGNAL( getSchedule( quint32 ) ),
             m_vdr,              SLOT( getSchedule( quint32 ) ) );
    connect( m_channelListModel, SIGNAL( getSchedule( quint32 , QDateTime, quint32 ) ),
             m_vdr,              SLOT( getSchedule( quint32 , QDateTime, quint32 ) ) );

    connect( m_vdr,              &VDR::scheduleAvailable,
             m_channelListModel, &ChannelListModel::updateSchedule );

    //Timerlist
    connect( this,  &VompRemote::getTimerList,
             m_vdr, &VDR::getTimerList );
    connect( m_vdr, &VDR::timersAvailable,
             this,  &VompRemote::updateTimers );

}


bool VompRemote::loginVDR()
{
    qDebug() << "VompRemote::loginVDR called";

    if( !m_vdrEnabled )
        return false;

    if( m_vdrAddress.isNull() )
    {
        emit messageError( tr( "Missing server address" ), tr( "No valid server address entered." ) );
        return false;
    }
    m_vdr->setVDRServerAddress( m_vdrAddress );

    m_serverTimeOffset = m_vdr->requestLogin();

    if ( m_vdr->state() == QAbstractSocket::ConnectedState )
    {
        emit getChannelList( 1 ); //1 is TV channels
        emit getTimerList();
    }
    else
    {
        return false;
    }

    return true;
}


void VompRemote::logoutVDR()
{
    m_vdr->requestLogout();
}


void VompRemote::vdrConnectionStateChanged()
{
    qDebug() << "VompRemote::connectionStateChanged, new state: " << m_vdr->state();

    emit vdrConnectedChanged();
}


void VompRemote::setRootObject( QObject *rootObject )
{
    m_rootObject = rootObject;

    QObject* child = rootObject->findChild<QObject*>("delegateItem");

    if ( child )
    {
        QObject::connect( child, SIGNAL( epgTriggerVisibleUpdate( int, int ) ),
                          m_channelListModel, SLOT( triggerEpgUpdate( int, int ) ) );
    }
}


void VompRemote::appendHost( QString name, QString host )
{
    if( QHostAddress( host ).isNull() )
    {
        qDebug() << "VompRemote::appendHost: Warning - invalid host address: " << host;
        return;
    }

    qDebug() << "VompRemote::appendHost host: " << name << "  with list count before: " << m_mvpList.count();
    m_mvpListModel->insertRow( m_mvpList.count() );
    m_mvpList[ m_mvpList.count() - 1 ].name = name;
    m_mvpList[ m_mvpList.count() - 1 ].ip = host;

    if ( m_currentHost == -1 )
    {
        m_currentHost = 0;
    }

    saveSettings();

    emit currentHostChanged();
    emit currentHostNameChanged();
}


void VompRemote::sendKey( int key )
{
    qDebug() << "VompRemote:sendKey called, with key: " << key;

    QString a;
    a.setNum( key );

    QByteArray datagram;
    datagram.append( a );

    if ( m_currentHost < 0 )
    {
        qDebug() << "VompRemote:sendKey: no host is defined, nothing sent";
        return;
    }

    QHostAddress ip = QHostAddress( m_mvpList.at( m_currentHost ).ip );
    qint64 written = m_udpSocket->writeDatagram( datagram,
                                                 ip,
                                                 MVPPORT );

    emit keySent();

    qDebug() << "C++: sent Key:" << key
             << "  with text: " << a
             << "  to: " << ip
             << "  with result: " << written;
}



void VompRemote::searchVdr()
{
    qDebug() << "VompRemote:searchVdr called";

//    m_vdrAddress = QHostAddress("192.168.1.10");
//    m_vdrAddress = QHostAddress();

    if ( searchForServer() ) //We have found one now, save setting and inform the user
    {
        saveSettings();
        emit serverAddressChanged();
        emit serverNameChanged();
    }
    else //Nothing found
    {
        emit messageError( tr( "Searching VDR" ), tr( "Couldn't find VDR server." ) );
        return;
    }
}


void VompRemote::setCurrentHost( int pos )
{
    if ( pos > m_mvpList.size() - 1 )
    {
        qDebug() << "setCurrentHost: Warning - pos out of range";
        pos = m_mvpList.size() - 1;
    }

    m_currentHost = pos;

    saveSettings();

    emit currentHostChanged();
    emit currentHostNameChanged();

    qDebug() << "setCurrentHost: Current Host changed new Pos: " << pos;
}


void VompRemote::updateChannels( const ChannelList & channelsList )
{
    qDebug() << "VompRemote:updateChannelsTV: size = " << channelsList.size();

    if( m_channelListModel->rowCount() > 0 )
        m_channelListModel->removeRows( 0, m_channelListModel->rowCount() - 1 );

    for ( int i = 0; i < channelsList.size(); ++i )
    {
        m_channelListModel->insertRow( i );

        if ( i > channelList()->count() - 1 || i < 0 )
        {
            qWarning() << "VompRemote::updateChannels::setChannelName: row is out of range:" << i
                       << "  and count:" << channelList()->count() - 1;
            return;
        }

        m_channelList[ i ] = channelsList[ i ];
    }

//    m_channelListSelectionModel->setCurrentIndex( m_channelListModel->index( 0, 0 ),
//                                                  QItemSelectionModel::SelectCurrent |
//                                                  QItemSelectionModel::Rows );
}


void VompRemote::updateTimers( const QList<TimerEntry> & timerList )
{
    qDebug() << "VompRemote::updateTimers: size = " << timerList.size();
    m_timerList = timerList;

    m_timerListModel->removeRows( 0, m_timerListModel->rowCount() );

    for ( int i = 0; i < timerList.size(); ++i )
    {
        m_timerListModel->insertRow( i );
        m_timerListModel->setTimerData( timerList[ i ], i );
    }

//    if ( timerList.size() > 0 )
//    {
//        m_timersWidget->timerListView()->setCurrentIndex( m_timersWidget->timerListView()->model()->index( 0, 0 ) );
//    }
}


QDateTime VompRemote::serverTime()
{
    QDateTime time = QDateTime::currentDateTime();
    time = time.addSecs( m_serverTimeOffset );

    return time;
}


void VompRemote::setColorButtonStyle( bool style )
{
    if ( m_colorButtonStyle == style )
        return;

    m_colorButtonStyle = style;

    saveSettings();

    emit colorButtonStyleChanged();

    qDebug() << "setColorButtonStyle: Color Button Style changed: " << style;
}


void VompRemote::setVdrEnabled( bool vdrEnabled )
{
    qDebug() << "VompRemote::setVdrEnabled enabled changed with old: " << m_vdrEnabled << "  and new: " << vdrEnabled;

    if ( m_vdrEnabled == vdrEnabled ) //nothing changed
        return;

    m_vdrEnabled = vdrEnabled;

    saveSettings();

    emit vdrEnabledChanged();

    if( m_vdrEnabled )
    {
        if ( m_vdr->state() == QAbstractSocket::ConnectedState )
        {
            qWarning() << "VompRemote::setVdrEnabled trying to enable, but we are already connected";
            return;
        }

        if ( !loginVDR() )
        {
            qWarning() << "VompRemote::setVdrEnabled trying to enable, but login failed";
            return;
        }
    }
    else
    {
        if ( m_vdr->state() != QAbstractSocket::ConnectedState )
        {
            qWarning() << "VompRemote::setVdrEnabled trying to disable, but we are already disconnected";
            return;
        }

        logoutVDR();
    }
}


void VompRemote::setVdrConnected( bool vdrConnected )
{
    qDebug() << "VompRemote::setVdrConnected with old: " << ( m_vdr->state() == QAbstractSocket::ConnectedState ) << "  and new: " << vdrConnected;

    if ( ( m_vdr->state() == QAbstractSocket::ConnectedState ) == vdrConnected ) //nothing changed
        return;

    if( m_vdrEnabled )
    {
        if ( m_vdr->state() == QAbstractSocket::ConnectedState )
        {
            qWarning() << "VompRemote::setVdrConnected trying to connect, but we are already connected";
            return;
        }

        if ( !loginVDR() )
        {
            qWarning() << "VompRemote::setVdrConnected trying to enable, but login failed";
            return;
        }
    }
}


void VompRemote::setServerName( QString name )
{
    qDebug() << "VompRemote::setServerAddress new VDR server name: " << name;

    if ( m_vdrName == name ) //nothing changed
        return;

    m_vdrName = name;
    emit serverNameChanged();
}


void VompRemote::setServerAddress( QString address )
{
    qDebug() << "VompRemote::setServerAddress new VDR server address: " << address;

    if ( m_vdrAddress == QHostAddress( address ) ) //nothing changed
        return;

    m_vdrAddress.setAddress( address );
    emit serverAddressChanged();
}


void VompRemote::removeHost( int pos )
{
    qDebug() << "removeHost called with pos " << pos << "  and list.size():" << m_mvpList.size();

    if( m_mvpList.size() == 0 )
    {
        return; // empty list
    }

    if ( pos > m_mvpList.size() - 1 )
    {
        qDebug() << "removeHost: Warning - pos out of range";
        pos = m_mvpList.size() - 1;
    }

    if ( m_currentHost >= pos ) //Selection pos is to be reduced
    {
        m_currentHost = m_currentHost - 1;
    }

    m_mvpListModel->removeRow( pos );

    saveSettings();

    emit currentHostChanged();
    emit currentHostNameChanged();
}


QString VompRemote::getCurrentHostName()
{
    if( m_mvpList.count() > 0 )
        return m_mvpList.at( m_currentHost ).name;
    else
        return QString();
}


void VompRemote::loadSettings()
{
    //The current host is saved as string, we need to find position in list
    QString host = m_settings->value( "CurrentHost" ).toString();
    m_currentHost = -1; //Default

    m_colorButtonStyle = m_settings->value( "ColorButtonStyle" ).toBool();

    m_vdrEnabled = m_settings->value( "VdrEnabled" ).toBool();

    m_vdrAddress = QHostAddress( m_settings->value( "VDRAddress" ).toString() );
    m_vdrName = m_settings->value( "VDRName" ).toString();

    int size = m_settings->beginReadArray( "mvpClients" );
    for ( int i = 0; i < size; ++i )
    {
        m_settings->setArrayIndex( i );

        QString name = m_settings->value( "MvpName" ).toString();
        QHostAddress ip = QHostAddress( m_settings->value( "ipAddress" ).toString() );

        if( ip.toString() == host ) //Current host found
        {
            m_currentHost = i;
        }
        m_mvpListModel->insertRow( m_mvpList.count() );
        m_mvpList[ m_mvpList.count() - 1 ].name = name;
        m_mvpList[ m_mvpList.count() - 1 ].ip = ip;

//        qDebug() << "MvpAdded name:" << name << "  count: " << m_mvpList.count();
    }
    m_settings->endArray();

    qDebug() << "VompRemote settings loaded";
}


void VompRemote::saveSettings()
{
    qDebug() << "VompRemote::saveSettings called";

    //First reset all settings
    m_settings->clear();

    //Current client, we store as string
    if( m_mvpList.size() > 0 )
    {
        QHostAddress host = QHostAddress( m_mvpList.at( m_currentHost ).ip );
        m_settings->setValue( "CurrentHost", host.toString() );
    }

    //Color Button Style
    m_settings->setValue( "ColorButtonStyle", m_colorButtonStyle );

    //Enabled VDR
    m_settings->setValue( "VdrEnabled", m_vdrEnabled );

    //ServerAdress of VDR
    m_settings->setValue( "VDRAddress", m_vdrAddress.toString() );
    m_settings->setValue( "VDRName", m_vdrName );

    //List of clients
    m_settings->beginWriteArray( "mvpClients" );
    for ( int i = 0; i < m_mvpList.size(); ++i )
    {
        m_settings->setArrayIndex( i );
        m_settings->setValue( "MvpName", m_mvpList.at( i ).name );
        m_settings->setValue( "ipAddress", m_mvpList.at( i ).ip.toString() );
    }
    m_settings->endArray();
}


bool VompRemote::searchForServer()
{
    qDebug() << "VompRemote::searchForServer()";

    if ( !m_vdrFindServer->socketEstablished() )
    {
        qWarning() << "Binding to socket 3024 failed. Maybe you have a VDR instance running on this machine.";
        messageError( tr("QtVomp"), tr("Couldn't bind to socket 3024.") );

        return false;
    }

    bool serverFound = false;
    int loops = 0;


    while ( !serverFound && loops < 5 )
    {
        loops++;

        m_serverList = m_vdrFindServer->serverList();

        if ( m_serverList.count() == 1 )
        {
            serverFound = true;

            m_vdrName = m_serverList[0].hostName;
            m_vdrAddress = m_serverList[0].hostAddress;
        }
        else
            if ( m_serverList.count() > 1 )
            {
                serverFound = true;

//            QDialog* dlg = new QDialog();
//            QVBoxLayout *vbox = new QVBoxLayout;
//            QButtonGroup *buttonGroup = new QButtonGroup( dlg );

//            QGroupBox *groupBox = new QGroupBox( "Select a server" );
//            groupBox->setFlat( true );

//            QPushButton* button;

//            for ( int i = 0; i < serverList.size(); ++i )
//            {
//                button = new QPushButton( serverList[i].hostName, dlg );
//                buttonGroup->addButton( button, i );
//                vbox->addWidget( button );
//            }

//            dlg->connect( buttonGroup, SIGNAL( buttonClicked( int ) ), dlg, SLOT( done( int ) ) );

//            vbox->addStretch( 1 );
//            groupBox->setLayout( vbox );

//            QVBoxLayout *vbox2 = new QVBoxLayout;
//            vbox2->addWidget( groupBox );

//            dlg->setLayout( vbox2 );
//            int result = dlg->exec();
//            server = serverList[result].hostAddress;

            m_vdrName = m_serverList[0].hostName;
            m_vdrAddress = m_serverList[0].hostAddress;
//            delete vbox;
//            delete vbox2;
//            delete dlg;
        }
    }
    return true;
}
